package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/hex"
	"fmt"
	"github.com/gin-gonic/gin"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"sync"
)

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)

	var ser = NewServer()

	r := gin.Default()
	r.Use(func(c *gin.Context) {
		data, err := c.GetRawData()
		if err != nil {
			return
		}
		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(data)) // 关键点
		log.Println(string(data))
		c.Next()
	})
	r.POST("transport", ser.transport)
	r.POST("server", ser.server)
	r.POST("client", ser.client)

	if err := r.Run(":8866"); err != nil {
		log.Println(err)
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////

type Address struct {
	Network string `json:"network" binding:"required"` // tcp/udp (no only support tcp)
	Address string `json:"address" binding:"required"`
	IsTLS   bool   `json:"is_tls"`
}

type TransportParams struct {
	Listen Address `json:"listen" binding:"required"`
	Target Address `json:"target" binding:"required"`
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////

type Server struct {
	transports sync.Map // network_port : TransportListenerInfo
	servers    sync.Map // network_port : ServerListenerInfo

	netUtil *NetUtil
}

func NewServer() *Server {
	return &Server{
		netUtil: NewNetUtil(),
	}
}

type TransportListenerInfo struct {
	Key    string          `json:"key"`
	Listen net.Listener    `json:"listen"`
	Info   TransportParams `json:"info"`
}

type ServerListenerInfo struct {
	Key    string       `json:"key"`
	Listen net.Listener `json:"listen"`
	Info   Address      `json:"info"`
}

func (tis *Server) transport(c *gin.Context) {
	var param TransportParams
	if err := c.ShouldBindJSON(&param); err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
			"msg": err.Error(),
			"example": TransportParams{
				Listen: Address{
					Network: "tcp",
					Address: ":8855",
					IsTLS:   false,
				},
				Target: Address{
					Network: "tcp",
					Address: "192.168.6.56:5588",
					IsTLS:   true,
				},
			},
		})
		return
	}

	listener, err := tis.netUtil.listen(&param.Listen)
	if err != nil {
		log.Println(err)
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
			"msg": err.Error(),
		})
		return
	}

	go tis.netUtil.transport(listener, &param.Target)

	value := TransportListenerInfo{
		Key:    fmt.Sprintf("%v_%v", param.Listen.Network, param.Listen.Address),
		Listen: listener,
		Info:   param,
	}
	tis.transports.Store(value.Key, value)

	c.JSON(http.StatusOK, gin.H{
		"msg": "success",
		"uid": value.Key,
	})
}

func (tis *Server) server(c *gin.Context) {
	var param Address
	if err := c.ShouldBindJSON(&param); err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
			"msg": err.Error(),
			"example": Address{
				Network: "tcp",
				Address: ":8855",
				IsTLS:   false,
			},
		})
		return
	}

	listener, err := tis.netUtil.listen(&param)
	if err != nil {
		log.Println(err)
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
			"msg": err.Error(),
		})
		return
	}

	go tis.netUtil.server(listener)

	value := ServerListenerInfo{
		Key:    fmt.Sprintf("%v_%v", param.Network, param.Address),
		Listen: listener,
		Info:   param,
	}
	tis.servers.Store(value.Key, value)

	c.JSON(http.StatusOK, gin.H{
		"msg": "success",
		"uid": value.Key,
	})
}

func (tis *Server) client(c *gin.Context) {
	var param Address
	if err := c.ShouldBindJSON(&param); err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
			"msg": err.Error(),
			"example": Address{
				Network: "tcp",
				Address: ":8855",
				IsTLS:   false,
			},
		})
		return
	}

	conn, err := tis.netUtil.newClientConnection(&param)
	if err != nil {
		log.Println(err)
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
			"msg": err.Error(),
		})
		return
	}

	go tis.netUtil.client(conn)

	c.JSON(http.StatusOK, gin.H{
		"msg": "success",
	})
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////

type NetUtil struct {
}

func NewNetUtil() *NetUtil {
	return &NetUtil{}
}

// transport 转发器
func (c *NetUtil) transport(listener net.Listener, target *Address) {
	log.Printf("listen %v", listener.Addr().String())
	defer func() {
		log.Printf("listen close %v", listener.Addr().String())
		_ = listener.Close()
	}()

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Println(err)
			return
		}
		log.Printf("listen %v new conn: %v", listener.Addr().String(), conn.RemoteAddr())

		go func() {
			newConn, err := c.newClientConnection(target)
			if err != nil {
				log.Println(err)
				return
			}

			var closeConnection = func() {
				if conn != nil {
					log.Printf("listen %v close conn: %v", listener.Addr().String(), conn.RemoteAddr())
					_ = conn.Close()
					conn = nil
				}
				if newConn != nil {
					_ = newConn.Close()
					newConn = nil
				}
			}

			go func() {
				defer closeConnection()

				io.Copy(conn, newConn)
			}()

			go func() {
				defer closeConnection()

				io.Copy(newConn, conn)
			}()
		}()
	}
}

// server 服务器
func (c *NetUtil) server(listener net.Listener) {
	log.Printf("listen %v", listener.Addr().String())
	defer func() {
		log.Printf("listen close %v", listener.Addr().String())
		_ = listener.Close()
	}()

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Println(err)
			return
		}
		log.Printf("listen %v new conn: %v", listener.Addr().String(), conn.RemoteAddr())

		go func() {
			defer func() {
				log.Printf("listen %v close conn: %v", listener.Addr().String(), conn.RemoteAddr())
				_ = conn.Close()
			}()

			buffer := make([]byte, 65535)
			for {
				n, err := conn.Read(buffer)
				if err != nil {
					return
				}

				log.Printf("hex: %v", hex.EncodeToString(buffer[:n]))
				log.Printf("str: %v", string(buffer[:n]))
			}
		}()
	}
}

// client 客户端
func (c *NetUtil) client(conn net.Conn) {
	log.Printf("conn %v", conn.RemoteAddr())
	defer func() {
		log.Printf("conn close %v", conn.RemoteAddr())
		_ = conn.Close()
	}()

	buffer := make([]byte, 65535)
	for {
		n, err := conn.Read(buffer)
		if err != nil {
			return
		}

		log.Printf("hex: %v", hex.EncodeToString(buffer[:n]))
		log.Printf("str: %v", string(buffer[:n]))
	}
}

// listen 监听
func (c *NetUtil) listen(listenParam *Address) (net.Listener, error) {
	if listenParam.IsTLS {
		return tls.Listen(listenParam.Network, listenParam.Address, c.getConfig())
	} else {
		return net.Listen(listenParam.Network, listenParam.Address)
	}
}

// newClientConnection 连接
func (c *NetUtil) newClientConnection(targetParam *Address) (net.Conn, error) {
	if targetParam.IsTLS {
		return tls.Dial(targetParam.Network, targetParam.Address, c.getConfig())
	} else {
		return net.Dial(targetParam.Network, targetParam.Address)
	}
}

// getConfig tls配置
func (c *NetUtil) getConfig() *tls.Config {
	cert, err := tls.X509KeyPair([]byte(certPem), []byte(keyPem))
	if err != nil {
		log.Fatal(err)
	}

	cfg := &tls.Config{
		Certificates: []tls.Certificate{cert},
		VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
			return nil
		},
		VerifyConnection: func(state tls.ConnectionState) error {
			return nil
		},
		InsecureSkipVerify: true,
	}

	return cfg
}

const (
	certPem = `-----BEGIN CERTIFICATE-----
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
6MF9+Yw1Yy0t
-----END CERTIFICATE-----`

	keyPem = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49
AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q
EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==
-----END EC PRIVATE KEY-----`
)
